/*
 * Copyright (c)  2015~2020, xforceplus
 * All rights reserved.
 * Project:datacare-orm
 * Id: SQLPlaceholderST.java   2020-10-10 21-32-56
 * Author: Evan
 */
package cn.datacare.orm.engine;

import cn.datacare.orm.type.JavaType;
import cn.datacare.orm.util.kit.StringKit;
import org.beetl.core.Context;
import org.beetl.core.exception.BeetlException;
import org.beetl.core.statement.*;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static cn.datacare.orm.constant.Constants.PARAM_PARAMS;
import static cn.datacare.orm.util.Separator.QUESTION_MARK;
import static cn.datacare.orm.util.StringUtils.EMPTY;

/**
 * <p>
 * Title:
 * </p>
 * <p>
 * Description:
 * </p>
 * <p>
 * Copyright: 2015~2020
 * </p>
 * <p>
 * Company/Department: xforceplus
 * </p>
 *
 * @author Evan
 * <b>Creation Time:</b> 2020-10-10 21-32-56
 * @since V1.0
 */
public class SQLPlaceholderST extends PlaceholderST {

    /**
     *  这些函数调用总是返回函数结果而不是一个sql占位符号“?”
     */
    public static final Set<String> TEXT_FUN_LIST = new HashSet<>();

    static {
        TEXT_FUN_LIST.add("text");
        TEXT_FUN_LIST.add("use");
        TEXT_FUN_LIST.add("globalUse");
        TEXT_FUN_LIST.add("join");
        TEXT_FUN_LIST.add("page");
        TEXT_FUN_LIST.add("orm.single");
        TEXT_FUN_LIST.add("orm.many");
    }

    public SQLPlaceholderST(Expression exp, FormatExpression format, GrammarToken token) {
        super(exp, format, token);


    }

    @Override
    public final void execute(Context ctx) {
        try {
            Object value = expression.evaluate(ctx);
            int jdbcType = 0;
            if (format != null) {
                String formatName = format.token.text;
                if (formatName.startsWith("typeof")) {
                    //特殊的format，告诉此对象应该作为jdbc类型
                    String str = formatName.substring(6).toLowerCase();
                    str = StringKit.unCapitalize(str);
                    Integer expectJdbcType = JavaType.jdbcTypeNames.get(str);
                    if (expectJdbcType == null) {
                        BeetlException be = new BeetlException(BeetlException.FORMAT_NOT_FOUND,
                                formatName + "是用来指示jdbc类型，并不存在，请检查java.sql.Type");
                        be.pushToken(format.token);
                        throw be;
                    }
                    jdbcType = expectJdbcType;
                } else if (formatName.equals("jdbc")) {
                    Integer expectJdbcType = (Integer) format.evaluateValue(value, ctx);
                    if (expectJdbcType == null) {
                        BeetlException be = new BeetlException(BeetlException.ERROR,
                                formatName + "是用来指示jdbc类型，并不存在，请检查java.sql.Type");
                        be.pushToken(format.token);
                        throw be;
                    }
                    jdbcType = expectJdbcType;

                } else {
                    value = format.evaluateValue(value, ctx);
                }


            }

            if (expression instanceof FunctionExpression) {
                //db 开头或者内置的方法直接输出
                FunctionExpression fun = (FunctionExpression) expression;
                String funName = fun.token.text;
                if (funName.startsWith("db")) {
                    ctx.byteWriter.writeString(value != null ? value.toString() : EMPTY);
                    return;
                } else if (TEXT_FUN_LIST.contains(funName)) {
                    ctx.byteWriter.writeString(value != null ? value.toString() : EMPTY);
                    return;
                }
            }
            int type = SQLParameter.NAME_EXPRESSION;
            ctx.byteWriter.writeString(QUESTION_MARK);
            List list = (List) ctx.getGlobal(PARAM_PARAMS);
            SQLParameter sqlPara = new SQLParameter(expression.token.text, value, type);
            sqlPara.setJdbcType(jdbcType);
            list.add(sqlPara);


        } catch (IOException e) {
            BeetlException be = new BeetlException(BeetlException.CLIENT_IO_ERROR_ERROR, e.getMessage(), e);
            be.pushToken(this.token);
            throw be;
        }

    }

}
